//
//  PulldownRefreshControl.swift
//  GZWeibo009
//
//  Created by gzxzmac on 16/3/28.
//  Copyright © 2016年 gzxzmac. All rights reserved.
//

import UIKit

/*
    你有封装过什么框架给给别人用?
        1.尽量少的依赖其他框架.
        2.不想给别人使用的属性或方法不要暴露出去
        3.高内聚,低耦合,尽量少的依赖其他对象
        4.使用简单,暴露的方法名称注意
*/

// MARK: - 扩展UIScrollView,动态添加下拉刷新控件属性,方便UIScrollView使用下拉刷新
private var pullDownRefreshControlKey = "pullDownRefreshControlKey"
extension UIScrollView {
    /// 计算型属性,计算型属性只是生成getter和setter方法
    /// 使用运行时,动态关联对象,操作起来和字典很想
    private var pullDownRefreshControl: PulldownRefreshControl {
        get {
            // 来对象身上根据key来去东西
            // object: AnyObject!: 要取东西的对象,
            // key: key,一定要记得加&
            if let refreshControl = objc_getAssociatedObject(self, &pullDownRefreshControlKey) as? PulldownRefreshControl {
                return refreshControl
            } else {
                // 在对象身上没找到需要的东西
                let refreshControl = PulldownRefreshControl()
                
                // 添加到scrollView上面
                self.addSubview(refreshControl)
                
                // 将refreshControl关联到self,会调用setter方法
                self.pullDownRefreshControl = refreshControl
                
                return refreshControl
            }
        }
        
        set {
            // 将newValue添加到self身上
            // bject: AnyObject!: 要添加东西的对象,
            // key: key,一定要记得加&
            // value: AnyObject!: 要添加的东西
            // policy: objc_AssociationPolicy: 策略
            objc_setAssociatedObject(self, &pullDownRefreshControlKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    // MARK: - 添加3个对外公开的方法,不让外部直接访问下拉刷新控件属性
    /// 添加开始刷新的闭包
    func addPullDownRefreshHandler(handler: () -> ()) {
        // 访问getter
        pullDownRefreshControl.pulldownRefreshHandler = handler
    }
    
    /// 开始刷新
    func startRefreshing() {
        // 访问getter
        pullDownRefreshControl.startRefreshing()
    }
    
    /// 结束刷新
    func endRefreshing() {
        // 访问getter
        pullDownRefreshControl.endRefreshing()
    }
}

private let PulldownRefreshControlHeight: CGFloat = 60

/// 刷新控件的3种状态
enum PulldownRefreshControlStatus: Int {
    case Normal         // 箭头朝下, 下拉刷新
    case PullToRefresh  // 箭头朝上, 释放刷新
    case Refreshing     // 正在刷新
}

class PulldownRefreshControl: UIView {
    
    // MARK: - 属性
    /// 刷新控件开始刷新的回调
    var pulldownRefreshHandler: (() -> ())?
    
    /// 父类,定义属性的时候属性类型后面!
    /// UIScrollView!: 表示可选类型,隐式拆包,在使用的时候可以不用强制拆包,需要保证在使用的时候一定有值.如果值为nil,也是会挂掉
    /// 和?有区别.UIScrollView?,后面使用需要强制拆包
    /// UIScrollView!: 后面使用不需要强制拆包.如果值为nil,也是会挂掉
    private weak var scrollView: UIScrollView!
    
    /// 记录上一次的状态
//    private var oldStatus: PulldownRefreshControlStatus = .Normal
    
    /// 刷新控件的当前刷新状态,默认是 Normal:箭头朝下, 下拉刷新
    private var currentStatus: PulldownRefreshControlStatus = .Normal {
        didSet {
            // 状态改变后来改变UI
            switch currentStatus {
            case .Normal:
                // 箭头转下来,文字变为: 下拉刷新
                // 移除风火轮的旋转动画
                fhlView.layer.removeAllAnimations()
                arrowView.hidden = false
                fhlView.hidden = true
                messageLabel.text = "下拉刷新"
                
                UIView.animateWithDuration(CZDefaultDuration, animations: { () -> Void in
                    self.arrowView.transform = CGAffineTransformIdentity
                })
                
//                // 只有上次是刷新状态,再切换到Normal状态的时候,才让tableView往上走刷新控件的高度,把刷新控件隐藏掉
//                if oldStatus == .Refreshing {
//                    UIView.animateWithDuration(CZDefaultDuration, animations: { () -> Void in
//                        self.scrollView.contentInset.top -= PulldownRefreshControlHeight
//                    })
//                }
            case .PullToRefresh:
                // 箭头旋转,文字变为: 释放刷新
                messageLabel.text = "释放刷新"
                UIView.animateWithDuration(CZDefaultDuration, animations: { () -> Void in
                    self.arrowView.transform = CGAffineTransformMakeRotation(CGFloat(M_PI))
                })
                
            case .Refreshing:
                // 箭头隐藏,文字变为: 正在刷新
                arrowView.hidden = true
                fhlView.hidden = false
                
                messageLabel.text = "正在刷新..."
                
                let rotation = CABasicAnimation(keyPath: "transform.rotation")
                rotation.toValue = M_PI * 2
                rotation.duration = 0.5
                rotation.repeatCount = MAXFLOAT
                
                // forKey:如果传nil,动画叠加, forKey:不等于nil,重新动画
                fhlView.layer.addAnimation(rotation, forKey: "rotation")
                
                // 修改scrollView.contentInset.top的值,让tableView不要滚动到原来的位置,需要在往下移动刷新控件的高度,就能显示刷新控件
                UIView.animateWithDuration(CZDefaultDuration, animations: { () -> Void in
                    self.scrollView.contentInset.top += PulldownRefreshControlHeight
                    }, completion: { (_) -> Void in
                        // 刷新控件动画到导航栏下方的时候调用
                        self.pulldownRefreshHandler?()
                })
            }
            
            // 需要在didSet最下面来赋值之前的状态
//            oldStatus = currentStatus
        }
    }
    
    // MARK: - 公开方法
    /// 开始刷新
    func startRefreshing() {
        currentStatus = .Refreshing
    }
    
    /// 结束刷新,别人加载到数据后来调用
    func endRefreshing() {
        currentStatus = .Normal
        
        UIView.animateWithDuration(CZDefaultDuration, animations: { () -> Void in
            self.scrollView.contentInset.top -= PulldownRefreshControlHeight
        })
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // 移除KVO
    deinit {
        scrollView.removeObserver(self, forKeyPath: "contentOffset")
    }

    /// 只要代码创建控件都会走这里,如果别人没有传frame,frame就是CGRectZero
    // 不管别人传入什么样的frame我都设置为我们自己的frame
    override init(frame: CGRect) {
        let newFrame = CGRect(x: 0, y: -PulldownRefreshControlHeight, width: UIScreen.width, height: PulldownRefreshControlHeight)
        super.init(frame: newFrame)
        prepareUI()
    }
    
    private func prepareUI() {
        backgroundColor = UIColor.clearColor()
        
        // 添加子控件
        addSubview(arrowView)
        addSubview(fhlView)
        addSubview(messageLabel)
        
        // 隐藏风火轮
        fhlView.hidden = true
        
        // 添加约束,如果想给比人用,最好不要依赖太多,尽量使用系统的API
        arrowView.translatesAutoresizingMaskIntoConstraints = false
        fhlView.translatesAutoresizingMaskIntoConstraints = false
        messageLabel.translatesAutoresizingMaskIntoConstraints = false
        
        // 箭头
        addConstraint(NSLayoutConstraint(item: arrowView, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: -30))
        addConstraint(NSLayoutConstraint(item: arrowView, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0))
        
        // 风火轮
        addConstraint(NSLayoutConstraint(item: fhlView, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: arrowView, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0))
        addConstraint(NSLayoutConstraint(item: fhlView, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: arrowView, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0))
        
        // messageLabel
        addConstraint(NSLayoutConstraint(item: messageLabel, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: arrowView, attribute: NSLayoutAttribute.Trailing, multiplier: 1, constant: 10))
        addConstraint(NSLayoutConstraint(item: messageLabel, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: arrowView, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0))
    }

    /// view即将被添加到父控件上面,当调用addSubView后会触发
    /// newSuperview: 父类
    override func willMoveToSuperview(newSuperview: UIView?) {
        // 记得调用super
        super.willMoveToSuperview(newSuperview)
        
        // KVO,监听父类对象属性的改变tableView.contentOffest
        if let scrollView = newSuperview as? UIScrollView {
            self.scrollView = scrollView
            
            // scrollView.监听的对象
            // observer: NSObject: 谁来监听
            // forKeyPath: 监听的属性
            // options: New, Old
            // context: 上下文
            scrollView.addObserver(self, forKeyPath: "contentOffset", options: [NSKeyValueObservingOptions.New, NSKeyValueObservingOptions.Old], context: nil)
        }
    }
    
    /*
        keyPath: Optional("contentOffset"), change: Optional(["kind": 1])
        keyPath: Optional("contentOffset"), change: Optional(["new": NSPoint: {0, -64}, "kind": 1])
        keyPath: Optional("contentOffset"), change: Optional(["old": NSPoint: {0, -64}, "new": NSPoint: {0, -64}, "kind": 1])
        PulldownRefreshControl.swift[67行], observeValueForKeyPath(_:ofObject:change:context:): scrollView.contentOffset: (0.0, -64.0)
    */
    
    /*
        Normal <-> PullToRefresh. 判断:contetnOffest.y = -64 - 60 = -124,还需要判断当前状态 -64. scrollView.ContentInset.Top
            1.Normal: contetnOffest.y > -124,  -120
            2.PullToRefresh: contetnOffest.y < -124, 130
    */
    /// KVO.当监听对象的属性发生改变的时候回调用
    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
//        CZPrint(msg: "keyPath: \(keyPath), change: \(change)")
//        CZPrint(msg: "scrollView.contentOffset: \(scrollView.contentOffset)")
        
        if scrollView.dragging {
            // scrollView正在处于手指推动状态
            // 手指拖动tableView, Normal <-> PullToRefresh之间的状态切换
            if currentStatus == .PullToRefresh && scrollView.contentOffset.y > -scrollView.contentInset.top - PulldownRefreshControlHeight {   // -64. -scrollView.contentInset.top - PulldownRefreshControlHeight
                CZPrint(msg: "切换到Normal")
                currentStatus = .Normal
            } else if currentStatus == .Normal && scrollView.contentOffset.y < -scrollView.contentInset.top - PulldownRefreshControlHeight {
                CZPrint(msg: "切换到PullToRefresh")
                currentStatus = .PullToRefresh
            }
        } else {
            if currentStatus == .PullToRefresh && scrollView.contentOffset.y > -scrollView.contentInset.top - PulldownRefreshControlHeight {
                // 手指松开
                // 当松手的时候,如果是PullToRefresh状态就切换到Refreshing 正在刷新
                CZPrint(msg: "切换到Refreshing")
                currentStatus = .Refreshing
            }
        }
    }
    
    // MARK: - 懒加载
    /// 箭头
    private lazy var arrowView: UIImageView = UIImageView(image: UIImage(named: "tableview_pull_refresh"))
    
    /// 风火轮
    private lazy var fhlView: UIImageView = UIImageView(image: UIImage(named: "tableview_loading"))
    
    private lazy var messageLabel: UILabel = {
        let label = UILabel()
        
        // 设置
        label.textColor = UIColor.darkGrayColor()
        label.font = UIFont.systemFontOfSize(14)
        label.text = "下拉刷新"
        
        return label
    }()
}
